/*
 * @Author: TC
 * @Date: 2025-01-24 14:14:44
 * @Last Modified by: TC
 * @Last Modified time: 2025-06-06 18:04:39
 */

import { AssetManager, assetManager, DynamicAtlasManager, ImageAsset, Texture2D } from "cc";
import { app } from "../../App";
import { nsEngineConst } from "./Const/EngineConst";
import { nsPrototypeUtil as nsPrototype } from "../../Util/PrototypeUtil";

let parsed = assetManager["_parsed"];
let assets = assetManager.assets;
let parsing: AssetManager.Cache;
let parser = assetManager.parser;
/**
 * 自定义引擎解析器
 */
export class CCParser {
    /**
     * 初始化
     */
    public init() {
        this._registerImageParser();
        this._handleTextureProto();
        DynamicAtlasManager.instance.enabled = false;
        let defParse = parser.parse;

        parsing = new AssetManager.Cache();
        parser.parse = (
            id: string,
            file: any,
            type: string,
            options: Record<string, any>,
            onComplete: (err: Error | null, data?: any | null) => void
        ) => {
            if (nsEngineConst.imageTypes.indexOf(type) > -1) {
                let uint8Array = new Uint8Array(file);
                let data = null;
                // uint8Array = this.decodeBuffer(this.head, uint8Array);
                let _id = id.slice(0, 36);
                const parsedAsset = parsed.get(id) || assets.get(_id);
                if (parsedAsset) {
                    onComplete(null, parsedAsset);
                    return;
                }
                const parsingAsset = parsing.get(id);
                if (parsingAsset) {
                    parsingAsset.push(onComplete);
                    return;
                }
                parsing.add(id, [onComplete]);
                let complete = function (err, data) {
                    if (err) {
                        onComplete && onComplete(err, null);
                        return;
                    }
                    const callbacks = parsing.remove(id);
                    for (let i = 0, l = callbacks!.length; i < l; i++) {
                        callbacks![i](err, data);
                    }
                };
                if (this.validHeader(uint8Array)) {
                    data = app.ktxMgr.parseBasis(uint8Array);
                    defParse.call(parser, id, data, type, options, complete);
                } else {
                    data = new Blob([uint8Array]);
                    defParse.call(parser, id, data, type, options, complete);
                }
            } else {
                defParse.call(parser, id, file, type, options, onComplete);
            }
        };
    }

    /**
     * 处理Texture2D的原型链
     */
    private _handleTextureProto(): void {
        nsPrototype.defineProperty(Texture2D, "image", {
            set(image) {
                if (image._nativeData.format) {
                    this.reset({
                        width: image._nativeData.width,
                        height: image._nativeData.height,
                        format: image._nativeData.format || image.format,
                    });
                    this.uploadData(image._nativeData.data);
                } else {
                    this.mipmaps = image ? [image] : [];
                }
            },
            get(): ImageAsset | null {
                return this._mipmaps.length === 0 ? null : this._mipmaps[0]._nativeData.data;
            },
            configurable: true,
            enumerable: true,
        });
        nsPrototype.modifyPrototype(Texture2D, "_setMipmapParams", function (images: any[]) {
            this._generatedMipmaps = images;
            this._setMipmapLevel(this._generatedMipmaps.length);
            if (this._generatedMipmaps.length > 0) {
                const imageAsset = this._generatedMipmaps[0];
                this.reset({
                    width: imageAsset._nativeData.width,
                    height: imageAsset._nativeData.height,
                    format: imageAsset._nativeData.format || imageAsset.format,
                });
                if (imageAsset._nativeData.format) {
                    this.uploadData(imageAsset._nativeData.data);
                }
                this._generatedMipmaps.forEach((mipmap, level) => {
                    this._assignImage(mipmap, level);
                });
                //
            } else {
                this.reset({
                    width: 0,
                    height: 0,
                    mipmapLevel: this._generatedMipmaps.length,
                    baseLevel: this._baseLevel,
                    maxLevel: this._maxLevel,
                });
            }
        });
    }

    /**
     * 注册图片解析器
     */
    private _registerImageParser(): void {
        for (let type of nsEngineConst.imageTypes) {
            parser.register(type, (file, options, onComplete) => {
                if (file instanceof HTMLImageElement) {
                    onComplete(null, file);
                    return;
                }
                createImageBitmap(file, { premultiplyAlpha: "none" }).then(
                    (result): void => {
                        onComplete(null, result);
                    },
                    (err): void => {
                        onComplete(null, file);
                    }
                );
            });
        }
    }
    private head = [0x73, 0x42, 0x13, 0, 0x4d, 0];
    validHeader(data: Uint8Array): boolean {
        // header size in bytes, sizeof(basis_file_header)
        let basis_head_size = 77;
        if (!data || data.byteLength <= basis_head_size) {
            return false;
        }
        let arr = data;
        let len = this.head.length;
        for (let i = 0; i < len; ++i) {
            if (arr[i] != this.head[i]) {
                return false;
            }
        }
        arr = null;
        return true;
    }
    decodeBuffer(key: number[], array: Uint8Array): Uint8Array {
        // array = array.slice(6, array.length);
        let len = array.length;
        let keyLen = key.length;
        for (let i = 0; i < keyLen; i++) {
            array[len - keyLen + i] ^= key[i];
        }
        return array;
    }
}
